<template>
  <div class="flowManageTop">
    <div class="left space-x-10px">
      <el-icon
        class="iconfont xfv-icon-back"
        style="cursor: pointer"
        @click="backFunc"
      />
      <el-divider direction="vertical" />
      <span class="scene-manage-title" :title="props.name">{{
        props.name
      }}</span>
    </div>
    <!-- <div class="right">
      <div class="btn-scroll-wrapper">
        <el-icon
          v-if="showLeftArrow"
          class="arrow left fixed-arrow"
          @click="scrollLeft"
        >
          <ArrowLeft />
        </el-icon>
        <div
          class="btn-scroll-content"
          ref="btnScrollContent"
          @scroll="updateArrowStatus"
          :style="arrowPaddingStyle"
        >
          <div class="btns-right">
            <el-button
              v-if="!props.isCopy"
              type="primary"
              @click="preViewGraphicFunc"
              >预览图谱</el-button
            >
            <el-button
              v-if="!props.isCopy"
              type="primary"
              @click="preViewTopoFunc"
              >预览拓扑</el-button
            >
            <el-button
              v-if="!props.isCopy"
              type="primary"
              @click="sceneAttackPath"
              >攻击路径</el-button
            >
            <el-button
              v-if="!props.isCopy"
              type="primary"
              @click="sceneSnapshot"
              >场景快照</el-button
            >
            <el-button v-if="!props.isCopy" type="primary" @click="sceneVersion"
              >场景版本</el-button
            >
            <el-button type="primary" @click="generatePictureFunc"
              >生成封面</el-button
            >
            <el-button type="primary" @click="generateSceneFunc"
              >生成环境</el-button
            >
            <el-button type="warning" @click="resetSceneFunc"
              >重置场景</el-button
            >
            <el-button type="primary" @click="openSceneFunc('start')"
              >开启</el-button
            >
            <el-button type="info" @click="openSceneFunc('stop')"
              >关闭</el-button
            >
            <el-button type="warning" @click="openSceneFunc('restart')"
              >重启</el-button
            >
          </div>
        </div>
        <el-icon
          v-if="showRightArrow"
          class="arrow right fixed-arrow"
          @click="scrollRight"
        >
          <ArrowRight />
        </el-icon>
      </div>

      <el-dialog
        v-model="preViewGraphicVisible"
        title="选择图谱"
        class="preview-topo-dialog"
      >
        <div class="flex preview-container">
          <div
            v-for="item in pdfList"
            class="preview-container-item"
            @click="confirmPreviewGraphic(item)"
          >
            <div class="pdf-img-container">
              <img src="@/assets/PDF/PDF.png" alt="" class="pdf-img" />
            </div>
            <div class="preview-title">
              <span :title="item.name">{{ item.name }}</span>
            </div>
          </div>
        </div>
      </el-dialog>

      <div class="preview-graphic-img-move"
           v-if="graphicShowVisible"
           :style="boxStyle1"
           @mousedown="startDrag1"
           @mousemove="onDrag1"
           @mouseup="stopDrag1($event, reloadPdf)"
           @mouseleave="stopDrag1">
        <el-icon color="#666" class="preview-topo-img-close" @click="graphicShowVisible = false"><CloseBold /></el-icon>
        <vue-office-pdf :src="graphicItem.component" class="pdf"></vue-office-pdf>
      </div>



      <el-dialog
        v-model="preViewTopoVisible"
        title="选择拓扑"
        class="preview-topo-dialog"
      >
        <el-form ref="formRef" :model="preViewTopoForm" class="m-auto mt-1">
          <div class="flex items-center preview-container">
            <div
              v-for="item in topoList"
              :key="item.ID"
              class="preview-container-item"
              @click="confirmPreviewTopo(item)"
            >
              <img :src="simulationCover(item.cover)" alt="" />
              <div class="preview-title">
                <span :title="item.name">{{ item.name }}</span>
              </div>
            </div>
          </div>
        </el-form>
      </el-dialog>

      <div
        class="preview-topo-img-move"
        v-if="topoShowVisible"
        :style="boxStyle"
        @mousedown="startDrag"
        @mousemove="onDrag"
        @mouseup="stopDrag"
        @mouseleave="stopDrag"
      >
        <el-icon
          color="#666"
          class="preview-topo-img-close"
          @click="topoShowVisible = false"
          ><CloseBold
        /></el-icon>
        <img :src="simulationCover(topoItem.cover)" alt="" />
      </div>

      <el-dialog
        v-model="dialogTableVisible"
        title="场景版本"
        class="scene-version-dialog"
      >
        <div style="margin-bottom: 10px">
          <el-button
            type="primary"
            @click="
              innerVisible = true;
              form = {};
            "
            >创建版本</el-button
          >
          <el-button
            :disabled="!selectRow"
            type="primary"
            @click="switchVersion"
            >切换</el-button
          >
          <el-button
            :disabled="!selectRow"
            type="primary"
            @click="uploadSceneLibrary"
            >导出到场景库</el-button
          >
          <el-button
            :disabled="!selectDeleteRow"
            type="danger"
            @click="delVersion"
            >删除</el-button
          >
        </div>
        <el-table
          ref="singleTableRef"
          highlight-current-row
          :data="versionData"
          style="width: 100%"
          @row-click="handleCurrentChange"
          row-key="ID"
          default-expand-all
          show-overflow-tooltip
        >
          <el-table-column
            align="center"
            property="name"
            label="名称"
            width="150"
          />
          <el-table-column
            align="center"
            property="CreatedAt"
            label="日期"
            width="200"
            :formatter="formatDate"
          />
          <el-table-column align="center" property="comment" label="描述" />
          <el-table-column align="center" width="150" label="副本状态">
            <template v-slot="{ row }">
              <div v-if="row.status || row.status === 0">
                <el-tag
                  :type="
                    row.status === 1
                      ? 'success'
                      : row.status === 2
                      ? 'danger'
                      : 'info'
                  "
                  >{{
                    row.status === 1
                      ? '已生成'
                      : row.status === 2
                      ? '生成失败'
                      : '未生成'
                  }}</el-tag
                >
              </div>
            </template>
          </el-table-column>
          <el-table-column align="center" width="120" label="任务状态">
            <template v-slot="{ row }">
              <div v-if="row.task">
                <el-result
                  v-if="row.task.status == 2 || row.task.status == 3"
                  :icon="row.task.status == 2 ? 'error' : 'success'"
                  class="resultProcess"
                />
                <el-button v-else loading type="primary" text  />
              </div>
              <div v-else>
                <el-result icon="info" class="resultProcess" />
              </div>
            </template>
          </el-table-column>
          <el-table-column align="center" property="viewTopo" label="查看拓扑">
            <template #default="{ row }">
              <el-icon
                style="margin-top: 12px; cursor: pointer"
                v-if="row.viewTopo !== 'hide'"
                @click="viewTopoFunc(row)"
                ><View
              /></el-icon>
              <span v-else></span>
            </template>
          </el-table-column>
        </el-table>
      </el-dialog>
      <el-dialog
        v-model="innerVisible"
        class="add-sceneVersion-dialog"
        top="18vh"
        title="创建版本"
      >
        <el-form ref="formRef" :model="form" :rules="rules" class="m-auto mt-1">
          <el-form-item prop="name" label="版本名称">
            <el-input v-model="form.name" />
          </el-form-item>
          <el-form-item label="版本描述">
            <el-input v-model="form.comment" type="textarea" />
          </el-form-item>
        </el-form>
        <template #footer>
          <span class="addIconFooter">
            <el-button @click="innerVisible = false">取 消</el-button>
            <el-button type="primary" @click="onSubmit">创 建</el-button>
          </span>
        </template>
      </el-dialog>
    </div> -->
  </div>

  <div>
    <el-dialog v-model="storageVisible" title="导出到场景库" class="storage-dialog">
      <el-form
          ref="storageFormRef"
          :rules="storageRules"
          label-position="right"
          class="mx-2 mt-1"
          :model="storageForm"
      >
        <el-form-item label="选择存储设备:" prop="equipment">
          <el-select filterable clearable v-model="storageForm.equipment">
            <el-option
                v-for="item in eqList"
                :key="item.id"
                :label="item.name"
                :value="item.id"
            ></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="editorToolFooter">
          <el-button  @click="storageVisible = false">取 消</el-button>
          <el-button type="primary" @click="saveStorageData">
            保 存
          </el-button>
        </span>
      </template>
    </el-dialog>
  </div>

  <viewTopo></viewTopo>
</template>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
import { generateScene, resetScene, openScene } from '@/api/topo/topo';
import {
  getSceneVersionList,
  createSceneVersion,
  deleteSceneVersion,
  switchSceneVersion,
  transferSceneVersion,
  getSceneVersionTopo,
} from '@/api/topo/version';
import {
  defineAsyncComponent,
  reactive,
  ref,
  watch,
  onMounted,
  nextTick,
  onUnmounted,
  computed,
} from 'vue';
import { dateFormat } from '@/utils/common';
import { useDraggable } from '@/hooks/useDraggable';
import VueOfficePdf from '@vue-office/pdf';
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue';

const ViewTopo = defineAsyncComponent(
  () => import('./viewTopo.vue'), // 异步加载组件
);

// 获取public下所有pdf
let files = null;
const pdfList = ref([]);

/**
 * 获取某文件夹下所有pdf
 */
const getFiles = () => {
  pdfList.value = [];

  getPublicPDF().then(res => {
    if (res.data && res.data.length > 0) {
      res.data.forEach(item => {
        pdfList.value.push({
          name: item.name,
          component: simulationCover(item.path),
        });
      });
    }
  });
};

const { boxStyle, startDrag, onDrag, stopDrag } = useDraggable(725, 12);
const {
  boxStyle: boxStyle1,
  startDrag: startDrag1,
  onDrag: onDrag1,
  stopDrag: stopDrag1,
} = useDraggable(545, 12);
const props = defineProps({
  name: {
    default: function () {
      return null;
    },
    type: String,
  },
  id: {
    default: function () {
      return null;
    },
    type: String,
  },
  isCopy: {
    default: false,
    type: Boolean,
  },
  sceneData: {
    default: function () {
      return null;
    },
    type: Object,
  },
});
const form = ref({});
const formRef = ref();
const dialogTableVisible = ref(false);
const innerVisible = ref(false);
const singleTableRef = ref(null);
const selectRow = ref(false);
const selectDeleteRow = ref(false);
const selectName = ref(null);
import { ElMessage, ElMessageBox } from 'element-plus';
import { emitter } from '@/utils/bus';
import { allCount } from '@/layout/notification/service/service';
import { getEngineTopology } from '@/api/topo/topology';
import { simulationCover } from '../defaultConfig';
import { getPublicPDF } from '@/api/topo/vmWare';
import {getEquipmentList} from "@/api/topo/component";
const emit = defineEmits([
  'generatePicture',
  'saveScene',
  'refresh',
  'closeDialog',
]);
const rules = {
  name: [{ required: true, trigger: 'blur', message: '版本名称不能为空' }],
};
const backFunc = () => {
  router.back();
};
const saveScene = async () => {
  emit('saveScene');
};
const versionData = ref([]);
const handleCurrentChange = row => {
  if (row.status || row.status === 0) {
    if (row.status === 1) {
      if (selectRow.value === row.ID) {
        singleTableRef.value.setCurrentRow();
        selectRow.value = false;
        selectDeleteRow.value = false;
        selectName.value = null;
      } else {
        selectRow.value = row.ID;
        selectDeleteRow.value = row.ID;
        selectName.value = row.name;
      }
    } else if (row.status === 2) {
      selectRow.value = false;
      if (selectDeleteRow.value === row.ID) {
        singleTableRef.value.setCurrentRow();
        selectName.value = null;
      } else {
        selectDeleteRow.value = row.ID;
        selectName.value = row.name;
      }
    } else if (row.status === 0) {
      if (row.task) {
        if (row.task.status === 1) {
          singleTableRef.value.setCurrentRow();
          selectRow.value = false;
          selectName.value = null;
          ElMessage.warning('该版本正在执行任务,不可选中！');
        }
      }
      selectRow.value = false;
      if (selectDeleteRow.value === row.ID) {
        singleTableRef.value.setCurrentRow();
        selectName.value = null;
      } else {
        selectDeleteRow.value = row.ID;
        selectName.value = row.name;
      }
    }
  } else {
    singleTableRef.value.setCurrentRow();
    selectRow.value = false;
    selectDeleteRow.value = false;
    selectName.value = null;
    ElMessage.warning('' + '当前状态不是版本');
  }
};

/**
 * 点击攻击路径
 */
const sceneAttackPath = () => {
  emitter.emit('sceneAttackPathDialog', props.id);
};

const sceneSnapshot = () => {
  emitter.emit('snapshotSceneDialog', props.id);
};

const sceneVersion = async () => {
  dialogTableVisible.value = true;
  await getVersionList();
};

const getVersionList = async () => {
  const res = await getSceneVersionList(props.id);
  versionData.value = res?.data || [];

  let flag = false;
  for (let i = 0; i < versionData.value.length; i++) {
    if (versionData.value[i].isSwitch) {
      versionData.value[i].children = [
        { name: '您在这里', comment: '当前场景位置', viewTopo: 'hide' },
      ];
      flag = true;
      break;
    }
  }

  if (!flag) {
    versionData.value.forEach(da => {
      if (da.isNow) {
        da.children = [
          { name: '您在这里', comment: '当前场景位置', viewTopo: 'hide' },
        ];
      }
    });
  }
};

const onSubmit = async () => {
  formRef.value &&
    formRef.value.validate(async valid => {
      if (valid) {
        const res = await createSceneVersion(props.id, {
          ...props.sceneData,
          name: form.value.name,
          comment: form.value.comment,
        });
        if (!(res instanceof Error)) {
          ElMessage.success(`生成成功`);
          innerVisible.value = false;
          await getVersionList();
        }
      }
    });
};

const taskFunc = data => {
  const { msg } = data;
  const info = msg.update || msg.create;
  allCount.value = msg.count;
  if (info) {
    const flg =
      versionData.value.length &&
      versionData.value.some(el => el.taskID === info.ID);
    if (flg) {
      getVersionList();
    }

    if (info.status === 2 || info.status === 3) {
      if (info.name.indexOf('快照') !== -1) {
        emitter.emit('updateSnapshotData');
      }
    }
  }
};
// 监听websocket消息通知
emitter.on('Task', taskFunc);
const delVersion = async () => {
  if (!selectDeleteRow.value) {
    return;
  }
  ElMessageBox.confirm(
    `删除场景版本 【${selectName.value}】 ，删除后将不可恢复！`,
    '删除场景版本',
    {
      confirmButtonText: '删 除',
      cancelButtonText: '取 消',
      type: 'warning',
      center: true,
      customClass: 'delMessageBox',
      confirmButtonClass: 'delButtonClass',
    },
  ).then(async () => {
    deleteSceneVersion(selectDeleteRow.value).then(res => {
      if (!(res instanceof Error)) {
        ElMessage.success(`删除成功!`);
        selectDeleteRow.value = false;
        selectName.value = null;
        getVersionList();
      }
    });
  });
};


// 存储设备相关
const eqList = ref([]);
const storageVisible = ref(false);
const storageFormRef = ref(null);
const storageRules = {
  equipment: [{ required: true, trigger: 'change', message: '请选择存储设备' }],
};

// 表单
const storageForm = reactive({
  equipment: null
});

const uploadSceneLibrary = async () => {
  if (!selectRow.value) {
    return;
  }

  storageVisible.value = true;

  // 获取存储设备
  getEquipmentList().then(res => {
    eqList.value = res.data.list;
  });
};

/**
 * 确定上传场景版本到资源库
 */
const saveStorageData = () =>{

  let equipment = {};
  eqList.value.forEach(item =>{
    if(storageForm.equipment === item.id) {
      equipment = item;
    }
  })

  storageFormRef.value.validate(valid => {
    if (valid) {
      transferSceneVersion(selectRow.value, equipment).then(res => {
        if (!(res instanceof Error)) {
          ElMessage.success(`上传中!`);
          getVersionList();
          storageVisible.value = false;
        }
      });
    }
  })
}

watch(storageVisible, () => {
  storageFormRef.value.resetFields()
  storageForm.equipment = null;
});

const switchVersion = async () => {
  if (!selectRow.value) {
    return;
  }
  ElMessageBox.confirm(
    `切换场景版本 【${selectName.value}】,切换后原场景将不可恢复！`,
    '切换场景版本',
    {
      confirmButtonText: '切 换',
      cancelButtonText: '取 消',
      type: 'warning',
      center: true,
      customClass: 'delMessageBox',
    },
  ).then(async () => {
    switchSceneVersion(selectRow.value).then(res => {
      if (!(res instanceof Error)) {
        if (res.data.taskID === '0') {
          ElMessage.success(`${res.msg}`);
          getVersionList();
          emit('refresh');
        } else {
          setTaskId(res.data.taskID);
        }
      }
    });
  });
};

const formatDate = (row, column) => {
  return dateFormat(row[column.property]);
};

const setTaskId = async id => {
  let mapList = sessionStorage.getItem('mapList') || '{}';
  mapList = JSON.parse(mapList);
  mapList[props.id] = id;
  sessionStorage.setItem('mapList', JSON.stringify(mapList));
};
const resetSceneFunc = async () => {
  ElMessageBox.confirm(`确认是否重置场景!`, {
    confirmButtonText: '确 定',
    cancelButtonText: '取 消',
    type: 'warning',
    center: true,
  }).then(async () => {
    let resDetail = await resetScene(props.id);
    if (!(resDetail instanceof Error)) {
      setTaskId(resDetail.data.taskID);
    }
  });
};
const generateSceneFunc = async () => {
  let resDetail = await generateScene(props);
  if (!(resDetail instanceof Error)) {
    setTaskId(resDetail.data.taskID);
  }
};
const openSceneFunc = async arg => {
  let resDetail = await openScene(props.id, { operation: arg });
  if (!(resDetail instanceof Error)) {
    setTaskId(resDetail.data.taskID);
  }
};
const generatePictureFunc = async () => {
  emit('generatePicture');
};

// 选择预览拓扑
const preViewTopoVisible = ref(false);
const topoShowVisible = ref(false);
const preViewTopoForm = reactive({
  topoId: '',
});
const topoList = ref([]);
const topoItem = ref({});
const dialogList = ref([]);

/**
 * 获取topo设计列表
 * @returns {Promise<void>}
 */
const preViewTopoFunc = async () => {
  preViewTopoVisible.value = true;

  const resEngine = await getEngineTopology();
  if (!(resEngine instanceof Error)) {
    topoList.value = resEngine.data;
  }
};

/**
 * 确定
 */
const confirmPreviewTopo = item => {
  topoShowVisible.value = true;
  topoItem.value = item;

  preViewTopoVisible.value = false;
  dialogList.value = [{}];
};

/**
 * 查看topo
 */
const viewTopoFunc = row => {
  getSceneVersionTopo(row.ID).then(res => {
    emitter.emit('viewTopo', {
      data: res,
      name: row.name,
    });
  });
};

watch(preViewTopoVisible, () => {
  preViewTopoForm.topoId = '';
});

// 知识图谱
const preViewGraphicVisible = ref(false);
const graphicShowVisible = ref(false);
const graphicItem = ref({});

// 预览图谱
const preViewGraphicFunc = () => {
  preViewGraphicVisible.value = true;

  getFiles();
};

/**
 * 确定选择图谱
 * @param item
 */
const confirmPreviewGraphic = item => {
  graphicShowVisible.value = true;
  graphicItem.value = item;

  preViewGraphicVisible.value = false;
};

/**
 * 重新渲染pdf
 */
const reloadPdf = () => {
  let temp = graphicItem.value;
  graphicItem.value = {};
  setTimeout(() => {
    graphicItem.value = temp;
  }, 100);
};

const btnScrollContent = ref(null);
const showLeftArrow = ref(false);
const showRightArrow = ref(false);

const updateArrowStatus = () => {
  const el = btnScrollContent.value;
  if (!el) return;
  showLeftArrow.value = el.scrollLeft > 0;
  showRightArrow.value = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
};

const scrollLeft = () => {
  const el = btnScrollContent.value;
  if (!el) return;
  el.scrollBy({ left: -150, behavior: 'smooth' });
  setTimeout(updateArrowStatus, 350);
};
const scrollRight = () => {
  const el = btnScrollContent.value;
  if (!el) return;
  el.scrollBy({ left: 150, behavior: 'smooth' });
  setTimeout(updateArrowStatus, 350);
};

// 增强 resize 监听，递归检测宽度变化
let resizeRafId = null;
const watchResize = () => {
  updateArrowStatus();
  resizeRafId = requestAnimationFrame(watchResize);
};

onMounted(() => {
  nextTick(() => {
    updateArrowStatus();
    watchResize();
  });
});

onUnmounted(() => {
  if (resizeRafId) cancelAnimationFrame(resizeRafId);
});

watch(
  [() => props.isCopy, () => props.name, () => props.id, () => props.sceneData],
  () => nextTick(updateArrowStatus),
  { deep: true },
);

const arrowPaddingStyle = computed(() => {
  return showLeftArrow.value || showRightArrow.value
    ? { padding: '0 32px' }
    : { padding: '0' };
});
</script>
<style lang="scss" scoped>
.flowManageTop {
  background-color: #ffffff;
  color: #e1efff;
  display: flex;
  align-items: center;
  height: 50px;
  .xfv-icon-back {
    color: #5b6775;
  }
  .scene-manage-title {
    display: inline-block;
    width: 320px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
    font-size: 16px;
    line-height: 1;
    color: #5b6775;
  }
  .left {
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .iconfont,
  .el-icon {
    font-size: 22px;
    line-height: 1;
    vertical-align: middle;
    cursor: pointer;
  }
}
</style>
<style lang="scss" scoped>
.flowManageTop {
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-radius: 4px;
  padding: 10px;
}

:deep(.scene-version-dialog) {
  width: 900px;

  .el-table {
    height: 340px !important;
  }

  .el-table__body-wrapper {
    height: 240px !important;
  }

  .el-empty__image {
    width: 108px !important;
  }
}

:deep(.add-sceneVersion-dialog) {
  width: 560px;
  .el-form {
    max-width: 460px;
    .el-form-item__label {
      width: 100px;
    }
  }
}

:deep(.preview-topo-dialog) {
  width: 560px;

  .preview-container {
    display: flex;
    flex-wrap: wrap;
    max-height: 460px;
    overflow-y: auto;
  }

  .preview-container-item {
    cursor: pointer;
    margin: 0 12px 20px 12px;
    img,
    .pdf-img-container {
      border: 1px solid #ecf5ff;
      width: 230px;
      height: 142px;
    }

    .pdf-img {
      border: none;
      width: 142px;
      margin: 0 auto;
    }

    .preview-title {
      color: #5b6775;
      background-color: #ecf5ff;
      padding: 5px 12px;
      text-align: center;
      width: 230px;
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;
    }
  }
}

:deep(.topo-show-dialog) {
  width: 280px;

  //.el-dialog__header {
  //  display: none;
  //}
}

.custom-preview {
  img {
    display: inline-block;
    height: 34px;
    float: right;
  }
}

.preview-graphic-img-move {
  position: absolute;
  z-index: 9999;
  box-shadow: rgb(204, 204, 204) 0px 0px 0.1rem;
  border-radius: 0.08rem;
  cursor: move;
  resize: both;
  overflow: auto;
  user-select: none;
  padding: 20px;
  background-color: #fff;
  width: 450px;
  height: 500px;

  .pdf {
    width: 100%;
    height: calc(100% - 30px);
    margin-top: 30px;
  }
}

.preview-topo-img-move {
  position: absolute;
  z-index: 9999;
  box-shadow: rgb(204, 204, 204) 0px 0px 0.1rem;
  border-radius: 0.08rem;
  cursor: move;
  resize: both;
  overflow: auto;
  user-select: none;
  padding: 20px;
  background-color: #fff;

  img {
    user-select: none;
    pointer-events: none;
    width: 100%;
    height: 100%;
  }
}

.preview-topo-img-close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 32px;
  height: 32px;
  cursor: pointer;
  color: #999;
  font-size: 18px;
}

/* 自定义手柄样式 */
.preview-topo-img-move::after,
.preview-graphic-img-move:after {
  content: '';
  position: absolute;
  bottom: 0;
  right: 0;
  width: 12px;
  height: 12px;
  border-bottom: 4px solid #9b9b9b;
  border-right: 4px solid #9b9b9b;
  cursor: se-resize;
  z-index: 10;
  margin-right: 2px;
  margin-bottom: 2px;
}

.right {
  width: 100%;
  min-width: 0;
  overflow: visible;
}

.btn-scroll-wrapper {
  position: relative;
  width: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
}

.btn-scroll-content {
  display: flex;
  flex-direction: row;
  white-space: nowrap;
  overflow-x: auto;
  min-width: 0;
  width: 100%;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.btn-scroll-content::-webkit-scrollbar {
  display: none;
}
.btn-scroll-content .el-button {
  // min-width: 120px;
  flex-shrink: 0;
}

.arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 10;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 0 8px #bbb;
  color: #409eff;
  font-size: 22px;
  cursor: pointer;
  user-select: none;
  transition: background 0.2s, color 0.2s;
  width: 22px;
}
.arrow.left {
  left: 0;
}
.arrow.right {
  right: 0;
}

.btns-right {
  display: flex;
  margin-left: auto;
  height: 50px;
  align-items: center;
}

:deep(.storage-dialog) {
  width: 560px;
}
</style>
